/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

	This file is part of eapmlib.

    Eapmlib is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or any 
	later version.

    Eapmlib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "DissociatedDipole.h"
#include "ImageData.h"
#include "IntImageData.h"

//#define _TEST_COMP

Evolutive::CDissociatedDipole::CDissociatedDipole(void) : CFeature(),m_MinArea(0),m_NumRegions(0),m_EvaluationType(DIPEVAL_MEAN)
{	
	//! Select the default minimum area (in number of pixels)
	m_MinArea=9;

	//! Number of regions of the feature
	m_NumRegions=2;
#ifndef USE_OPENCV
	OPENCV_ERROR_CLASS("CDissociatedDipole");
#endif
}

Evolutive::CDissociatedDipole::~CDissociatedDipole(void)
{	
}

#ifdef USE_OPENCV
void Evolutive::CDissociatedDipole::SetEvalType(Evolutive::DIPEVAL_METHOD Value)
{
	m_EvaluationType=Value;
}

void Evolutive::CDissociatedDipole::SetMinArea(int Value)
{
	m_MinArea=Value;
}

void Evolutive::CDissociatedDipole::SetParameters(int NumParameters, double *DblParameters)
{
	int Polarity;
	register int i;
	unsigned int ParametersBuffer[15];
	unsigned int *pParameters=NULL;

	// The parameters vector must have the following format:
	//
	//  |-----------------------------------------------------------------------------------------------
	//  |        Dipole1       |       Dipole2        || Polarity || Weights || Rotated   || Channels  | 
	//  |-----------------------------------------------------------------------------------------------
	//  | 0  1    2      3     | 4  5    6      7     ||    8     || 9   10  || 11   12   ||  13  14   |
	//  |-----------------------------------------------------------------------------------------------
	//  | x  y  width  height  | x  y  width  height  ||    P     || W1  W2  || R1   R2   ||  C1  C2   |
	//  |-----------------------------------------------------------------------------------------------
	// Check the pointer
	if(!DblParameters)
		throw CEvolutiveLibException("NULL pointer to the parameters",__FILE__,__LINE__,"SetParameters");

	// The dipoles can be defined with different number of parameters, using default values for not given values.
	if(NumParameters !=5 && NumParameters!=8 && NumParameters!=9 && NumParameters!=11 && NumParameters!=13 && NumParameters!=15)
		throw CEvolutiveLibException("Incorrect number of parameters",__FILE__,__LINE__,"SetParameters");

	// When the parameters are 5, it is a haar-like feature. Convert to dipoles format
	if(NumParameters==5)
	{
		//TODO: Add the 6 parameters case where polarity value is considered for Haar-like features
		GetDipoleFromHaar(DblParameters,ParametersBuffer);
		pParameters=ParametersBuffer;
	}
	else
	{
		// Store the double valued parameters to the unsigned int parameters array
		for(i=0;i<NumParameters;i++)
			ParametersBuffer[i]=static_cast<unsigned int>(DblParameters[i]);
		pParameters=ParametersBuffer;
	}

	// Obtain the information of the regions
	m_Dipole[0].Region.x=pParameters[0];
	m_Dipole[0].Region.y=pParameters[1];
	m_Dipole[0].Region.width=pParameters[2];
	m_Dipole[0].Region.height=pParameters[3];
	m_Dipole[1].Region.x=pParameters[4];
	m_Dipole[1].Region.y=pParameters[5];
	m_Dipole[1].Region.width=pParameters[6];
	m_Dipole[1].Region.height=pParameters[7];

	// Use the polarity information
	if(NumParameters>8)
	{
		Polarity=pParameters[8];
	}
	else
	{
		Polarity=0;
	}

	// Use the Weights information (this value came with a range [0,1], pass it to [1,2]
	if(NumParameters>9)
	{
		if(pParameters[9]>1 || pParameters[10]>1)
			throw CEvolutiveLibException("Feature Weight out of range",__FILE__,__LINE__,"SetParameters");
		m_Dipole[0].Weight=pParameters[9]+1;
		m_Dipole[1].Weight=pParameters[10]+1;
	}
	else
	{
		m_Dipole[0].Weight=1;
		m_Dipole[1].Weight=1;
	}

	// Use the rotation information
	if(NumParameters>11)
	{
		m_Dipole[0].Rotated=(bool)pParameters[11];
		m_Dipole[1].Rotated=(bool)pParameters[12];
	}
	else
	{
		m_Dipole[0].Rotated=false;
		m_Dipole[1].Rotated=false;
	}

	// Use the channel information
	if(NumParameters>13)
	{
		m_Dipole[0].Channel=pParameters[13];
		m_Dipole[1].Channel=pParameters[14];
	}
	else
	{
		m_Dipole[0].Channel=0;
		m_Dipole[1].Channel=0;
	}

	// Apply the polarity value
	if(!Polarity)
	{		
		m_Dipole[1].Weight*=-1;
	}
	else
	{
		m_Dipole[0].Weight*=-1;
	}
}

bool Evolutive::CDissociatedDipole::IsValid(CvSize WinSize,bool UseColor)
{
	register int i;

	// Verify all the regions
	for(i=0;i<m_NumRegions;i++)
	{
		//! Verify the area
		if(m_Dipole[i].Region.width*m_Dipole[i].Region.height<m_MinArea)
		{
			return false;
		}

		// Verify the size and position
		if(m_Dipole[i].Region.x<0 || m_Dipole[i].Region.y<0 ||  		  
		   m_Dipole[i].Region.width<0 || m_Dipole[i].Region.height<0 ||
		   m_Dipole[i].Region.x+m_Dipole[i].Region.width>=WinSize.width ||
		   m_Dipole[i].Region.y+m_Dipole[i].Region.height>=WinSize.height)
		{
			return false;
		}

		// Verify the channel
		if(m_Dipole[i].Channel>0 && !UseColor)
		{
			return false;
		}

		// Verify the weights
		if(abs(m_Dipole[i].Weight)>2)
		{
			return false;
		}
	}

	return true;
}

void Evolutive::CDissociatedDipole::DrawFeature(IplImage *Image)
{
	bool UseColor;
	CvSize ImgSize;
	CvPoint Dpt1,Dpt2;
	register int i;

	//! Obtain the input image size
	ImgSize=cvGetSize(Image);

	//! Obtain the color information
	if(Image->nChannels>1)
		UseColor=true;
	else
		UseColor=false;

	//! If the feature is a valid feature draw it
	if(IsValid(ImgSize,UseColor))
	{
		for(i=0;i<m_NumRegions;i++)
		{
			//! Create the extrema points for each region
			Dpt1.x=m_Dipole[i].Region.x;		
			Dpt1.y=m_Dipole[i].Region.y;
			Dpt2.x=m_Dipole[i].Region.x+m_Dipole[i].Region.width;
			Dpt2.y=m_Dipole[i].Region.y+m_Dipole[i].Region.height;			

			//! Draw the regions
			if(UseColor)
			{
				if(m_Dipole[i].Weight>0)				
					cvRectangle(Image,Dpt1,Dpt2,CV_RGB(0,0,255),2);					
				else
					cvRectangle(Image,Dpt1,Dpt2,CV_RGB(255,0,0),2);				
			}
			else
			{
				if(m_Dipole[i].Weight>0)				
					cvRectangle(Image,Dpt1,Dpt2,CV_RGB(255,255,255),1);					
				else
					cvRectangle(Image,Dpt1,Dpt2,CV_RGB(0,0,0),1);
			}
		}
	}
}

double Evolutive::CDissociatedDipole::Apply(CData *InputData)
{
	IplImage *pIntegral=NULL,*pRIntegral=NULL,*pSqIntegral=NULL;
	double ReturnVal;
	bool Valid=false;
	int Offset=0;

	if(InputData->GetDataName()==string("IMAGE"))
	{
		//! Point to the input data
		CImageData *pData=(CImageData*)InputData;

		//! Obtain a pointer to the image
		IplImage *pImage=pData->GetImage();

		//! Select the integral images size
		CvSize IntSize=cvGetSize(pImage);
		IntSize.width+=1;
		IntSize.height+=1;

		//! Create the integral images
		pIntegral=cvCreateImage(IntSize,IPL_DEPTH_32S,1);
		pRIntegral=cvCreateImage(IntSize,IPL_DEPTH_32S,1);
		pSqIntegral=cvCreateImage(IntSize,IPL_DEPTH_64F,1);

		// Calculate the integral image
		cvIntegral(pImage,pIntegral,pSqIntegral,pRIntegral);	

		// If a ROI is selected, calculate the offset in the integral images
		Offset=0;
		if(pData->GetOffset()>0)
		{
			Offset=((pData->GetROI()->y)*(pIntegral->widthStep/4.0))+pData->GetROI()->x;
		}

		//! Apply the method
		ReturnVal=Apply(pIntegral,pRIntegral,Offset,pData->GetScale());

		//! Free the images
		if(pIntegral)
			cvReleaseImage(&pIntegral);
		if(pRIntegral)
			cvReleaseImage(&pRIntegral);
		if(pSqIntegral)
			cvReleaseImage(&pSqIntegral);
		
		//! Set the valid flag to true
		Valid=true;
	}

	if(InputData->GetDataName()==string("INTEGRAL IMAGE"))
	{
		//! Point to the input data
		CIntImageData *pData=(CIntImageData*)InputData;

		//! Point to the images
		pIntegral=pData->GetIntImage();
		pRIntegral=pData->GetRotatedIntImage();

		//! Apply the method
		ReturnVal=Apply(pIntegral,pRIntegral,pData->GetOffset(),pData->GetScale());

		//! Set the valid flag to true
		Valid=true;
	}

	//! Check if the data type is valid
	if(!Valid)
	{
		throw CEvolutiveLibException("Unsupported data type",__FILE__,__LINE__,"Apply");
	}

	return ReturnVal;
}

double Evolutive::CDissociatedDipole::Apply(IplImage *IntImage,IplImage *RIntImage,int Offset,double Scale)
{
	register int i;
	unsigned int D0,D1,D2,D3;
	double Value,Aux;			
	int *pData=NULL,*pRData=NULL;
	int WidthStep;
	int MaxDesp;

	// Verify the feature
	if(!IsValid(cvSize((IntImage->width-1)/Scale,(IntImage->height-1)/Scale),IntImage->nChannels>1))
		return 0.0;

	// Select the width step (in pixels, not in bytes)
	WidthStep=IntImage->widthStep/4.0;	
	MaxDesp=WidthStep*IntImage->height;

	// Point to the integral images
	pData=(int*)IntImage->imageData;
	if(RIntImage)
		pRData=(int*)RIntImage->imageData;

	// Eval each region
	Value=0;
	for(i=0;i<m_NumRegions;i++)
	{
		// Calculate the displacements
		if(Scale!=1.0)
		{
			D0=cvRound(Scale*(m_Dipole[i].Region.x+m_Dipole[i].Region.y*WidthStep))+Offset;
			D1=D0+cvRound(Scale*m_Dipole[i].Region.width);
			D2=D0+cvRound(Scale*m_Dipole[i].Region.height*WidthStep);
			D3=D2+cvRound(Scale*m_Dipole[i].Region.width);
		}
		else
		{
			D0=(m_Dipole[i].Region.x+m_Dipole[i].Region.y*WidthStep)+Offset;
			D1=D0+m_Dipole[i].Region.width;
			D2=D0+m_Dipole[i].Region.height*WidthStep;
			D3=D2+m_Dipole[i].Region.width;
		}

		// Check the displacements to avoid memory errors
		if(D0>=MaxDesp || D1>=MaxDesp || D2>=MaxDesp || D3>=MaxDesp)
			throw CEvolutiveLibException("Displacement out of range",__FILE__,__LINE__,"Apply");
#ifdef _TEST_COMP
cout << "    " << i << ".- [" << D0 << "," << D1 << "," << D2 << "," << D3 << "] => ";
#endif
		// Apply the channel selection
		if(m_Dipole[i].Channel!=0)
		{
			throw CEvolutiveLibException("Color dipoles are not supported yet",__FILE__,__LINE__,"Apply");
		}
		
		// Evaluate the region
		if(!m_Dipole[i].Rotated)
		{
			Aux=pData[D0]+pData[D3]-(pData[D1]+pData[D2]);
		}
		else
		{
			if(RIntImage)
			{
				Aux=pRData[D0]+pRData[D3]-(pRData[D1]+pRData[D2]);
			}
			else
			{
				throw CEvolutiveLibException("NULL rotated integral image.",__FILE__,__LINE__,"Apply");
			}
		}
#ifdef _TEST_COMP
cout << Aux;
#endif
		// If the evaluation type is the mean, divide by the area
		if(m_EvaluationType==DIPEVAL_MEAN)
		{
			Aux/=(m_Dipole[i].Region.width*m_Dipole[i].Region.height)*(Scale*Scale);
		}
#ifdef _TEST_COMP
cout << "(" << Aux << ")" << endl;
#endif

		// Apply the weight
		Aux*=m_Dipole[i].Weight;	

		// Add to the final value
		Value+=Aux;
	}
#ifdef _TEST_COMP
cout << "    Value = " << Value << endl;
#endif
	return Value;
}


Evolutive::CDipoleStruct* Evolutive::CDissociatedDipole::GetData(int &NumRegions)
{
	// Set the number of regions
	NumRegions=m_NumRegions;

	// Return a pointer to the regions
	return m_Dipole;
}

int Evolutive::CDissociatedDipole::GetNumParameters(void)
{
	return 15;
}

void Evolutive::CDissociatedDipole::GetParameters(double *Parameters)
{
	// The parameters vector have the following format:
	//
	//  |-----------------------------------------------------------------------------------------------
	//  |        Dipole1       |       Dipole2        || Polarity || Weights || Rotated   || Channels  | 
	//  |-----------------------------------------------------------------------------------------------
	//  | 0  1    2      3     | 4  5    6      7     ||    8     || 9   10  || 11   12   ||  13  14   |
	//  |-----------------------------------------------------------------------------------------------
	//  | x  y  width  height  | x  y  width  height  ||    P     || W1  W2  || R1   R2   ||  C1  C2   |
	//  |-----------------------------------------------------------------------------------------------

	// Check the output pointer
	if(!Parameters)
		throw CEvolutiveLibException("NULL pointer to the parameters",__FILE__,__LINE__,"GetParameters");

	// Copy the parameters 
	Parameters[0]=static_cast<double>(m_Dipole[0].Region.x);
	Parameters[1]=static_cast<double>(m_Dipole[0].Region.y);
	Parameters[2]=static_cast<double>(m_Dipole[0].Region.width);
	Parameters[3]=static_cast<double>(m_Dipole[0].Region.height);
	Parameters[4]=static_cast<double>(m_Dipole[1].Region.x);
	Parameters[5]=static_cast<double>(m_Dipole[1].Region.y);
	Parameters[6]=static_cast<double>(m_Dipole[1].Region.width);
	Parameters[7]=static_cast<double>(m_Dipole[1].Region.height);

	// Obtain the polarity value from the region weights
	if(m_Dipole[0].Weight>m_Dipole[1].Weight)
		Parameters[8]=0.0;
	else	
		Parameters[8]=1.0;

	// Take the absolute value of the weights (the polarity value contains the sign)
	Parameters[9]=static_cast<double>(abs(m_Dipole[0].Weight)-1);
	Parameters[10]=static_cast<double>(abs(m_Dipole[1].Weight)-1);

	Parameters[11]=static_cast<double>(m_Dipole[0].Rotated);
	Parameters[12]=static_cast<double>(m_Dipole[1].Rotated);
	Parameters[13]=static_cast<double>(m_Dipole[0].Channel);
	Parameters[14]=static_cast<double>(m_Dipole[1].Channel);
}

void Evolutive::CDissociatedDipole::GetDipoleFromHaar(double* ParsIN,unsigned int *ParsOUT)
{	
	// Haar-like representation
	switch(static_cast<int>(ParsIN[4]))
	{
	case 0:
		// horizontal 2 rectangles NEG-POS
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2]);// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3]);// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0])+static_cast<unsigned int>(ParsIN[2]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1]);// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3]);// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=1;//Weight Reg1
		ParsOUT[10]=2;//Weight Reg2
		break;
	case 1:
		// horizontal 2 rectangles POS-NEG
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2]);// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3]);// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0])+static_cast<unsigned int>(ParsIN[2]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1]);// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3]);// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=2;//Weight Reg1
		ParsOUT[10]=1;//Weight Reg2
		break;
	case 2:
		// vertical 2 rectangles NEG-POS
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2]);// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3]);// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1])+static_cast<unsigned int>(ParsIN[3]);// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3]);// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=1;//Weight Reg1
		ParsOUT[10]=2;//Weight Reg2
		break;
	case 3:
		// vertical 2 rectangles POS-NEG
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2]);// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3]);// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1])+static_cast<unsigned int>(ParsIN[3]);// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3]);// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=2;//Weight Reg1
		ParsOUT[10]=1;//Weight Reg2
		break;
	case 4:
		// horizontal 3 rectangles POS-NEG-POS
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2])*3;// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3]);// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0])+static_cast<unsigned int>(ParsIN[2]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1]);// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3]);// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=2;//Weight Reg1
		ParsOUT[10]=0;//Weight Reg2
		break;
	case 5:
		// vertical 3 rectangles POS-NEG-POS
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2]);// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3])*3;// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1])+static_cast<unsigned int>(ParsIN[3]);// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3]);// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=2;//Weight Reg1
		ParsOUT[10]=0;//Weight Reg2		
		break;
	case 6:
		// horizontal 4 rectangles POS-NEG-NEG-POS
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2])*4;// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3]);// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0])+static_cast<unsigned int>(ParsIN[2]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1])*2;// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3]);// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=2;//Weight Reg1
		ParsOUT[10]=0;//Weight Reg2		
		break;
	case 7:
		// vertical 4 rectangles POS-NEG-NEG-POS
		ParsOUT[0]=static_cast<unsigned int>(ParsIN[0]);// x Reg1
		ParsOUT[1]=static_cast<unsigned int>(ParsIN[1]);// y Reg1
		ParsOUT[2]=static_cast<unsigned int>(ParsIN[2]);// w Reg1
		ParsOUT[3]=static_cast<unsigned int>(ParsIN[3])*4;// h Reg1
		ParsOUT[4]=static_cast<unsigned int>(ParsIN[0]);// x Reg2
		ParsOUT[5]=static_cast<unsigned int>(ParsIN[1])+static_cast<unsigned int>(ParsIN[3]);// y Reg2
		ParsOUT[6]=static_cast<unsigned int>(ParsIN[2]);// w Reg2
		ParsOUT[7]=static_cast<unsigned int>(ParsIN[3])*2;// h Reg2
		ParsOUT[8]=0;//Polarity
		ParsOUT[9]=2;//Weight Reg1
		ParsOUT[10]=0;//Weight Reg2		
		break;
	default:
		throw CEvolutiveLibException("Incorrect Haar-like type",__FILE__,__LINE__,"GetDipoleFromHaar");
	}		
}

void Evolutive::CDissociatedDipole::Serialize(std::fstream &fs,bool Saving)
{
	// Call the base class method
	CFeature::Serialize(fs,Saving);

	// Implement the specialized code
	if(Saving)
	{
		// Save the data to a file
		// Save the class attributes
		fs.write((char*)&m_MinArea,sizeof(int));
		fs.write((char*)&m_NumRegions,sizeof(int));	
		fs.write((char*)&m_EvaluationType,sizeof(DIPEVAL_METHOD));
		fs.write((char*)m_Dipole,m_NumRegions*sizeof(CDipoleStruct));
	}
	else
	{
		// Load the data from a file
		// Load the class attributes
		fs.read((char*)&m_MinArea,sizeof(int));
		fs.read((char*)&m_NumRegions,sizeof(int));	
		fs.read((char*)&m_EvaluationType,sizeof(DIPEVAL_METHOD));	
		fs.read((char*)m_Dipole,m_NumRegions*sizeof(CDipoleStruct));
	}	
}
#endif //USE_OPENCV